﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

using LowLevelGraphics;
using LowLevelGraphics.Filter;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Optimized Convolution using doubles
    /// </summary>
    public class OwnOptimizedConvolutionDouble : BaseImageFilter
    {
        protected int m_nDiv = 1;
        protected int m_nOffset = 0;

        protected double[,] m_aKernel =
            new double[,]
            {
                {0,0,0,0,0},
                {0,0,0,0,0},
                {0,0,1,0,0},
                {0,0,0,-1,0},
                {0,0,0,0,0},
            };

        protected bool m_bR = true;
        protected bool m_bG = true;
        protected bool m_bB = true;

        /// <summary>
        /// Initializes a new instance of the <see cref="OwnOptimizedConvolutionDouble"/> class.
        /// </summary>
        public OwnOptimizedConvolutionDouble()
        {
            m_nDiv = (int)Kernelsum;
            m_nOffset = 128;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="OwnOptimizedConvolutionDouble"/> class.
        /// </summary>
        /// <param name="_aKernel">The _a kernel.</param>
        public OwnOptimizedConvolutionDouble(double[,] _aKernel)
            : this(0, 1, _aKernel)
        {
            m_nDiv = (int)Kernelsum;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="OwnOptimizedConvolutionDouble"/> class.
        /// </summary>
        /// <param name="_nOffset">The _n offset.</param>
        /// <param name="_nDiv">The _n div.</param>
        /// <param name="_aKernel">The _a kernel.</param>
        public OwnOptimizedConvolutionDouble(int _nOffset, int _nDiv, double[,] _aKernel)
        {
            m_nDiv = _nDiv;
            m_nOffset = _nOffset;
            m_aKernel = _aKernel;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="OwnOptimizedConvolutionDouble"/> class.
        /// </summary>
        /// <param name="_myOwnOptimizedConvolution">The _my own optimized convolution.</param>
        internal OwnOptimizedConvolutionDouble(OwnOptimizedConvolutionDouble _myOwnOptimizedConvolution)
        {
            this.m_nDiv = _myOwnOptimizedConvolution.m_nDiv;
            this.m_nOffset = _myOwnOptimizedConvolution.m_nOffset;
            this.m_aKernel = _myOwnOptimizedConvolution.m_aKernel;
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="OwnOptimizedConvolution"/> is R.
        /// </summary>
        /// <value><c>true</c> if R; otherwise, <c>false</c>.</value>
        public bool R
        {
            get { return m_bR; }
            set { m_bR = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="OwnOptimizedConvolutionDouble"/> is G.
        /// </summary>
        /// <value><c>true</c> if G; otherwise, <c>false</c>.</value>
        public bool G
        {
            get { return m_bG; }
            set { m_bG = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="OwnOptimizedConvolutionDouble"/> is B.
        /// </summary>
        /// <value><c>true</c> if B; otherwise, <c>false</c>.</value>
        public bool B
        {
            get { return m_bR; }
            set { m_bR = value; }
        }

        /// <summary>
        /// Gets or sets the kernel.
        /// </summary>
        /// <value>The kernel.</value>
        public double[,] Kernel
        {
            get { return m_aKernel; }
            set { m_aKernel = value; }
        }

        /// <summary>
        /// Gets the kernelsum.
        /// </summary>
        /// <value>The kernelsum.</value>
        public double Kernelsum
        {
            get
            {
                double dSum = 0;
                foreach (double dValue in m_aKernel)
                {
                    dSum += dValue;
                }
                if (dSum == 0) dSum = 1.0f;
                return dSum;
            }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            int nTest = 0;
            UnsafeBitmap bitmapCloned = (UnsafeBitmap)_bitmap.Clone();

            Color color = Color.Transparent;
            Color colorKernel = Color.Transparent;

            int nLength = (int)Math.Sqrt(m_aKernel.LongLength);
            int nHeight = _bitmap.Height;
            int nWidth = _bitmap.Width;

            int y = 0;
            int x = 0;
            int ky = 0;
            int kx = 0;

            double nR = 0;
            double nG = 0;
            double nB = 0;

            int nColor = 0;
            int[] aValue = new int[m_aKernel.LongLength];
            int nIndex = 0;

            int nXTemp = 0;
            int nYTemp = 0;

            int nKernelMiddleX = (m_aKernel.GetLength(0) >> 1) + 1;
            int nKernelMiddleY = (m_aKernel.GetLength(1) >> 1) + 1;

            int[, ,] aSum = new int[nLength, nLength, 3];

            byte r = 0;
            byte g = 0;
            byte b = 0;

            for (y = 0; y < nHeight - nLength; y++)
            {
                for (x = 0; x < nWidth - nLength; x++)
                {
                    nR = 0;
                    nG = 0;
                    nB = 0;

                    nIndex = 0;

                    for (ky = 0; ky < nLength; ky++)
                    {
                        for (kx = 0; kx < nLength; kx++)
                        {
                            if (m_aKernel[kx, ky] == 0) continue;
                            nXTemp = x + kx;
                            nYTemp = y + kx;
                            colorKernel = bitmapCloned.GetPixel(nXTemp, nYTemp);

                            //apply only to selected channels
                            if (m_bR) nR += colorKernel.R * m_aKernel[kx, ky];
                            if (m_bG) nG += colorKernel.G * m_aKernel[kx, ky];
                            if (m_bB) nB += colorKernel.B * m_aKernel[kx, ky];
                            aValue[nIndex] = ColorFunctions.ToIntRGB(colorKernel);

                            nIndex++;
                        }
                    }

                    nR = nR / m_nDiv + m_nOffset;
                    nG = nG / m_nDiv + m_nOffset;
                    nB = nB / m_nDiv + m_nOffset;

                    if (nR < 0) nR = 0;
                    if (nG < 0) nG = 0;
                    if (nB < 0) nB = 0;
                    if (nR > 255) nR = 255;
                    if (nG > 255) nG = 255;
                    if (nB > 255) nB = 255;

                    //channels
                    //color = _bitmap.GetPixel(x + nKernelMiddleX, y + nKernelMiddleY);

                    if (!m_bR) nR = color.R;
                    if (!m_bG) nG = color.G;
                    if (!m_bB) nB = color.B;

                    _bitmap.SetPixel(x + nKernelMiddleX, y + nKernelMiddleY, Color.FromArgb((int)nR, (int)nG, (int)nB));
                }
            }
            bitmapCloned.Dispose();
            return _bitmap;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new OwnOptimizedConvolutionDouble(this);
        }
    }
}
